package cn.bigcontainer.alt.meta.service.impl;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import cn.bigcontainer.alt.common.entity.BigMap;
import cn.bigcontainer.alt.common.util.WoKit;
import cn.bigcontainer.alt.meta.dao.MDao;
import cn.bigcontainer.alt.meta.dto.MObjectDto;
import cn.bigcontainer.alt.meta.exception.MException;
import cn.bigcontainer.alt.meta.mapper.MAppMapper;
import cn.bigcontainer.alt.meta.mapper.MObjectMapper;
import cn.bigcontainer.alt.meta.mapper.MViewMapper;
import cn.bigcontainer.alt.meta.po.MApp;
import cn.bigcontainer.alt.meta.po.MButton;
import cn.bigcontainer.alt.meta.po.MObject;
import cn.bigcontainer.alt.meta.po.MProperty;
import cn.bigcontainer.alt.meta.po.MRelation;
import cn.bigcontainer.alt.meta.po.MView;
import cn.bigcontainer.alt.meta.po.MViewProperty;
import cn.bigcontainer.alt.meta.service.MetaManService;
import cn.bigcontainer.alt.meta.service.MetaService;

@Service
@Transactional
public class MetaManServiceImpl implements MetaManService {

    @Resource
    private MDao mDao;

    @Resource
    private MObjectMapper objectMapper;


    /**
     * 拆分property
     *
     * @param property property
     * @return props
     */
    private String[] spinOffProperty(String property) {
        String[] props = new String[2];
        int i = property.indexOf(".");
        if (i < 0) {
            return property.split("[.]");
        } else {
            props[0] = property.substring(0, i);
            props[1] = property.substring(i + 1);
            return props;
        }
    }

    /**
     * 获取mView集合
     *
     * @param objectId objectId
     * @return mView
     */
    private List<MView> getMView(String objectId) {

        List<MView> mViews = mViewMapper.selectList(new QueryWrapper<MView>().
                eq("object_id", objectId));
        return mViews;

    }


    @Override
    public void saveMObject(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            String sql = "update m_object set " + property + "='" + value + "' where id = '" + id + "'";
            mDao.update(sql);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]) || "columns".equals(props[0]))) {
                throw new MException(MException.META_SAVE);
            }
            MObject object = objectMapper.selectById(id);
            if ("data".equals(props[0])) {
                object.getData().put(props[1], value);
            } else {
                object.getColumns().put(props[1], value);
            }
            objectMapper.updateById(object);
        } else {
            throw new MException(MException.META_SAVE);
        }
    }

    @Override
    public void deleteMObject(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_ADD);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]) || "columns".equals(props[0]))) {
                throw new MException(MException.META_ADD);
            }
            MObject object = objectMapper.selectById(id);
            if ("data".equals(props[0])) {
                object.getData().remove(props[1]);
            } else {
                object.getColumns().remove(props[1]);
            }
            objectMapper.updateById(object);
        } else {
            throw new MException(MException.META_ADD);
        }
    }

    @Override
    public void addMObject(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_DETELE);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]) || "columns".equals(props[0]))) {
                throw new MException(MException.META_ADD);
            }
            MObject object = objectMapper.selectById(id);
            if ("data".equals(props[0])) {
                object.getData().put(props[1], value);
            } else {
                object.getColumns().put(props[1], value);
            }
            objectMapper.updateById(object);
        } else {
            throw new MException(MException.META_ADD);
        }
    }

    @Override
    public void saveMProperty(String objectId, String id, String property, String value) {
        MObject mObject = objectMapper.selectById(objectId);
        List<MProperty> properties = mObject.getProperties();
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            for (MProperty m : properties) {
                if (m.getId().equals(id)) {
                    WoKit.setField(m, property, value);
                }
            }
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_SAVE);
            }
            for (MProperty m : properties) {
                if (m.getId().equals(id)) {
                    m.getData().put(props[1], value);
                }
            }
        } else {
            throw new MException(MException.META_SAVE);
        }
        objectMapper.updateById(mObject);
    }

    @Override
    public void deleteMProperty(String objectId, String id, String property, String value) {
        MObject mObject = objectMapper.selectById(objectId);
        List<MProperty> properties = mObject.getProperties();
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_DETELE);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_DETELE);
            }
            for (MProperty m : properties) {
                if (m.getId().equals(id)) {
                    m.getData().remove(props[1]);
                    objectMapper.updateById(mObject);
                }
            }
        } else {
            throw new MException(MException.META_DETELE);
        }
    }

    @Override
    public void addMProperty(String objectId, String id, String property, String value) {
        MObject mObject = objectMapper.selectById(objectId);
        List<MProperty> properties = mObject.getProperties();
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_ADD);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_ADD);
            }
            for (MProperty m : properties) {
                if (m.getId().equals(id)) {
                    m.getData().put(props[1], value);
                    objectMapper.updateById(mObject);
                }
            }
        } else {
            throw new MException(MException.META_ADD);
        }
    }


    @Override
    public void saveMRelation(String objectId, String id, String property, String value) {
        MObject object = objectMapper.selectById(objectId);
        List<MRelation> relations = object.getRelations();
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            for (MRelation m : relations) {
                if (m.getId().equals(id)) {
                    WoKit.setField(m, property, value);
                }
            }
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_SAVE);
            }
            for (MRelation m : relations) {
                if (m.getId().equals(id)) {
                    m.getData().put(props[1], value);
                }
            }
        } else {
            throw new MException(MException.META_SAVE);
        }
        objectMapper.updateById(object);
    }

    @Override
    public void deleteMRelation(String objectId, String id, String property, String value) {
        MObject object = objectMapper.selectById(objectId);
        List<MRelation> relations = object.getRelations();
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_DETELE);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_DETELE);
            }
            for (MRelation m : relations) {
                if (m.getId().equals(id)) {
                    m.getData().remove(props[1]);
                    objectMapper.updateById(object);
                }
            }
        } else {
            throw new MException(MException.META_DETELE);
        }
    }

    @Override
    public void addMRelation(String objectId, String id, String property, String value) {
        MObject object = objectMapper.selectById(objectId);
        List<MRelation> relations = object.getRelations();
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_ADD);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_ADD);
            }
            for (MRelation m : relations) {
                if (m.getId().equals(id)) {
                    m.getData().put(props[1], value);
                    objectMapper.updateById(object);
                }
            }
        } else {
            throw new MException(MException.META_ADD);
        }
    }

    @Resource
    private MAppMapper mAppMapper;

    @Override
    public void saveMApp(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            String sql = "update m_app set " + property + "='" + value + "' where id = '" + id + "'";
            mDao.update(sql);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_SAVE);
            }
            MApp mApp = mAppMapper.selectById(id);
            if ("data".equals(props[0])) {
                mApp.getData().put(props[1], value);
            }
            mAppMapper.updateById(mApp);
        } else {
            throw new MException(MException.META_SAVE);
        }
    }

    @Override
    public void deleteMApp(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_DETELE);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_DETELE);
            }
            MApp mApp = mAppMapper.selectById(id);
            if ("data".equals(props[0])) {
                mApp.getData().remove(props[1]);
            }
            mAppMapper.updateById(mApp);
        } else {
            throw new MException(MException.META_DETELE);
        }
    }


    @Override
    public void addMApp(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_ADD);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_ADD);
            }
            MApp mApp = mAppMapper.selectById(id);
            if ("data".equals(props[0])) {
                mApp.getData().put(props[1], value);
            }
            mAppMapper.updateById(mApp);
        } else {
            throw new MException(MException.META_ADD);
        }
    }

    @Resource
    private MViewMapper mViewMapper;

    @Override
    public void saveMView(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            String sql = "update m_view set " + property + "='" + value + "' where id = '" + id + "'";
            mDao.update(sql);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_SAVE);
            }
            MView mView = mViewMapper.selectById(id);
            if ("data".equals(props[0])) {
                mView.getData().put(props[1], value);
            }
            mViewMapper.updateById(mView);
        } else {
            throw new MException(MException.META_SAVE);
        }
    }

    @Override
    public void deleteMView(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_DETELE);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_DETELE);
            }
            MView mView = mViewMapper.selectById(id);
            if ("data".equals(props[0])) {
                mView.getData().remove(props[1]);
            }
            mViewMapper.updateById(mView);
        } else {
            throw new MException(MException.META_DETELE);
        }
    }

    @Override
    public void addMView(String objectId, String id, String property, String value) {
        String[] props = spinOffProperty(property);
        if (props.length == 1) {
            throw new MException(MException.META_ADD);
        } else if (props.length == 2) {
            if (!("data".equals(props[0]))) {
                throw new MException(MException.META_ADD);
            }
            MView mView = mViewMapper.selectById(id);
            if ("data".equals(props[0])) {
                mView.getData().put(props[1], value);
            }
            mViewMapper.updateById(mView);
        } else {
            throw new MException(MException.META_ADD);
        }
    }

    private Map<String, String> parseConfig (String viewPropertyConfig) {
        Map<String, String> config = new HashMap<>();
        if (!WoKit.isEmpty(viewPropertyConfig)) {
            String[] keyOrValues = viewPropertyConfig.split("[,:]");
            if (keyOrValues.length % 2 == 1) {
                throw new MException(MException.META_CONFIG);
            }
            for (int i = 0; i < keyOrValues.length; i += 2) {
                config.put(keyOrValues[i], keyOrValues[i+1]);
            }
        }
        return config;
    }

    @Override
    public void addMViewPropertyByMProperty (String viewId, String propertyId, String viewPropertyType, String viewPropertyConfig) {
        Map<String, String> config = parseConfig(viewPropertyConfig);
        MView view = this.mViewMapper.selectById(viewId);
        MObject obj = objectMapper.selectById(view.getObjectId());
        MProperty prop = MObjectDto.getProperty(obj, propertyId);
        MViewProperty viewProperty = new MViewProperty();
        viewProperty.setId(viewId + "_" + prop.getName().toLowerCase());
        viewProperty.setIndex(WoKit.getInt(config.get("索引"), prop.getIndex()));
        viewProperty.setName(prop.getDescription());
        viewProperty.setType(viewPropertyType);
        viewProperty.setPropertyId(propertyId);
        viewProperty.setAllowsBlank(1 - prop.getMandatory());
        viewProperty.setColSpan(1);
        viewProperty.setRowSpan(1);
        viewProperty.setSubmit(1);
        viewProperty.setReadOnly((prop.getKey() == 1) ? 1 : 0);
        if (config.get("数据字典") != null) {
        	viewProperty.setDictionaryType(config.get("数据字典"));
        }
        List<MViewProperty> viewProperties = view.getProperties();
        viewProperties.add(viewProperty);
        Collections.sort(viewProperties);
        mViewMapper.updateById(view);
    }

    @Override
    public void addMViewPropertyByMRelation (String viewId, String relationId, String viewPropertyType, String viewPropertyConfig) {
        Map<String, String> config = parseConfig(viewPropertyConfig);
        MView view = this.mViewMapper.selectById(viewId);
        MObject obj = objectMapper.selectById(view.getObjectId());
        MRelation relation = MObjectDto.getRelation(obj, relationId);
        MViewProperty viewProperty = new MViewProperty();
        viewProperty.setId(viewId + "_" + relation.getName().toLowerCase());
        viewProperty.setIndex(WoKit.getInt(config.get("索引"), relation.getIndex()));
        viewProperty.setName(relation.getName());
        viewProperty.setRelationId(relation.getId());
        viewProperty.setType(viewPropertyType);
        // viewProperty.setAllowsBlank(1 - prop.getMandatory());
        viewProperty.setColSpan(1);
        viewProperty.setRowSpan(1);
        viewProperty.setSubmit(1);
        // viewProperty.setReadonly((prop.getKey() == 1) ? 1 : 0);
        List<MViewProperty> viewProperties = view.getProperties();
        viewProperties.add(viewProperty);
        Collections.sort(viewProperties);
        mViewMapper.updateById(view);
    }

    @Override
    public void saveMViewProperty(String objectId, String id, String property, String value) {
        List<MView> mViews = getMView(objectId);
        for (MView mView : mViews) {
            List<MViewProperty> properties = mView.getProperties();
            String[] props = spinOffProperty(property);
            if (props.length == 1) {
                for (MViewProperty m : properties) {
                    if (m.getId().equals(id)) {
                        WoKit.setField(m, property, value);
                    }
                }
            } else if (props.length == 2) {

                if (!("data".equals(props[0]))) {
                    throw new MException(MException.META_SAVE);
                }
                for (MViewProperty m : properties) {
                    if (m.getId().equals(id)) {
                        m.getData().put(props[1], value);
                    }
                }

            } else {
                throw new MException(MException.META_SAVE);
            }
            mViewMapper.updateById(mView);
        }


    }

    @Override
    public void deleteMViewProperty(String objectId, String id, String property, String value) {
        List<MView> mViews = getMView(objectId);
        for (MView mView : mViews) {
            List<MViewProperty> properties = mView.getProperties();
            String[] props = spinOffProperty(property);
            if (props.length == 1) {
                throw new MException(MException.META_DETELE);
            } else if (props.length == 2) {
                if (!("data".equals(props[0]))) {
                    throw new MException(MException.META_DETELE);
                }
                for (MViewProperty m : properties) {
                    if (m.getId().equals(id)) {
                        m.getData().remove(props[1]);
                        mViewMapper.updateById(mView);
                    }
                }
            } else {
                throw new MException(MException.META_DETELE);
            }
        }

    }

    @Override
    public void addMViewProperty(String objectId, String id, String property, String value) {
        List<MView> mViews = getMView(objectId);
        for (MView mView : mViews) {
            List<MViewProperty> properties = mView.getProperties();
            String[] props = spinOffProperty(property);
            if (props.length == 1) {
                throw new MException(MException.META_ADD);
            } else if (props.length == 2) {
                if (!("data".equals(props[0]))) {
                    throw new MException(MException.META_ADD);
                }
                for (MViewProperty m : properties) {
                    if (m.getId().equals(id)) {
                        m.getData().put(props[1], value);
                        mViewMapper.updateById(mView);
                    }
                }
            } else {
                throw new MException(MException.META_ADD);
            }
        }

    }

    @Override
    public void saveMButton(String objectId, String id, String property, String value) {
        List<MView> mViews = getMView(objectId);
        for (MView mView : mViews) {
            List<MButton> buttons = mView.getButtons();
            String[] props = spinOffProperty(property);
            if (props.length == 1) {
                for (MButton m : buttons) {
                    if (m.getId().equals(id)) {
                        WoKit.setField(m, property, value);
                    }
                }
            } else if (props.length == 2) {
            	if (!Arrays.asList("data", "request").contains(props[0])) {
                    throw new MException(MException.META_SAVE);
                }
                for (MButton m : buttons) {
                    if (m.getId().equals(id)) {
                    	if ("data".equals(props[0])) {
                    		m.getData().put(props[1], value);
                    	} else if ("request".equals(props[0])) {
                    		m.getRequest().put(props[1], value);
                    	}
                    }
                }
            } else {
                throw new MException(MException.META_SAVE);
            }
            mViewMapper.updateById(mView);
        }

    }

    @Override
    public void deleteMButton(String objectId, String id, String property, String value) {
        List<MView> mViews = getMView(objectId);
        for (MView mView : mViews) {
            List<MButton> buttons = mView.getButtons();
            String[] props = spinOffProperty(property);
            if (props.length == 1) {
                throw new MException(MException.META_DETELE);
            } else if (props.length == 2) {
                if (!("data".equals(props[0]))) {
                    throw new MException(MException.META_DETELE);
                }
                for (MButton m : buttons) {
                    if (m.getId().equals(id)) {
                        m.getData().remove(props[1]);
                        mViewMapper.updateById(mView);
                    }
                }
            } else {
                throw new MException(MException.META_DETELE);
            }
        }

    }

    @Override
    public void addMButton(String objectId, String id, String property, String value) {
        List<MView> mViews = getMView(objectId);
        for (MView mView : mViews) {
            List<MButton> buttons = mView.getButtons();
            String[] props = spinOffProperty(property);
            if (props.length == 1) {
                throw new MException(MException.META_ADD);
            } else if (props.length == 2) {
                if (!Arrays.asList("data", "request").contains(props[0])) {
                    throw new MException(MException.META_ADD);
                }
                for (MButton m : buttons) {
                    if (m.getId().equals(id)) {
                    	if ("data".equals(props[0])) {
                    		m.getData().put(props[1], value);
                    	} else if ("request".equals(props[0])) {
                    		m.getRequest().put(props[1], value);
                    	}
                        mViewMapper.updateById(mView);
                    }
                }
            } else {
                throw new MException(MException.META_ADD);
            }
        }
    }

    @Override
    public void setMeta(String id) {
        MObject mObject = objectMapper.selectById(id);
        BigMap<String, Object> data = mObject.getData();
        Object mate = data.get("meta");
        if (mate == null){
            data.put("meta","true");
        }else {
            data.remove("meta");
        }
        objectMapper.updateById(mObject);
    }

    @Resource
    private MetaService metaService;
    
	@Override
	public void copyApp(String id) {
		metaService.copyCrud(id);
	}

	@Override
	public void setMetaAll() {
		objectMapper.selectList(null).forEach(o -> {
			o.getData().bigPut("meta", "true");
			objectMapper.updateById(o);
		});
	}

	@Override
	public void unsetMetaAll() {
		objectMapper.selectList(null).forEach(o -> {
			o.getData().remove("meta");
			objectMapper.updateById(o);
		});
	}

    @Override
    public void deleteApp(String id) {
        metaService.deleteApp(id);
    }

    @Override
    public void deleteObject(String id) {
        metaService.deleteObject(id);
    }

}
